/*******************************************************************************
 * @file bsp_i2c.c
 * @brief I2C Interface driver
 *
 * @version 0.0.1
 *******************************************************************************
 * @license Refer License or other description Docs
 * @author  Felix
 ******************************************************************************/

/*******************************************************************************
History: revision  date  author

v0.0.1
  1. The first revision.

*******************************************************************************/
#include "cpu/bsp_i2c.h"
#include "i2c.h"

/*******************************************************************************
 * @brief
 *   Wait I2C transfer complete.
 ******************************************************************************/
static inline bool I2C_Transfer(M0P_I2C_TypeDef* enCh, I2C_TransferState state)
{
    uint32_t err = 0, err2 = 0;
    bool success = true;

    while(0 == I2C_GetIrq(enCh)){
        if(err++ > 5000){
            success = false;
            break;
        }
    }

    while(state != I2C_GetState(enCh)){
        if(err2++ > 5000){
            success = false;
            break;
        }
    }

    return success;
}

static uint32_t BSP_I2C_CalcBaud(I2C_BaudrateMode bMode)
{
    uint32_t baudrate = 1e5;
    switch(bMode){
    case I2C_BAUD_FAST:
        baudrate = 4e5;
        break;
    case I2C_BAUD_MAX:
        baudrate = 1e6;
        break;
    case I2C_BAUD_NORMAL:
    default:
        baudrate = 1e5;
        break;
    }

    return baudrate;
}

static bool I2C_MasterSlaveRW(M0P_I2C_TypeDef* enCh, uint8_t addr, bool read)
{
    /* Slave Write */
    I2C_TransferState state = i2cStateAddrWFAckNack;

    if(read){
        addr |= 0x01;
    }

    I2C_WriteByte(enCh, addr);
    I2C_ClearIrq(enCh);
    if(read){
        state = i2cStateRAddrWFAckNack;  /* Slave Read */
    }

    return I2C_Transfer(enCh, state);
}

static bool I2C_MasterWrite(M0P_I2C_TypeDef* enCh, uint8_t addr, uint8_t *data, uint32_t len)
{
    uint32_t i = 0;
    bool success = false;

    /* Wait slave success */
    success = I2C_MasterSlaveRW(enCh, addr, false);

    /* Wait write success */
    for(; i < len; i++){
        I2C_WriteByte(enCh, data[i]);
        I2C_ClearIrq(enCh);
        if(!I2C_Transfer(enCh, i2cStateDataSend)){
            success = false;
        }
    }

    return success;
}

static bool I2C_MasterRead(M0P_I2C_TypeDef* enCh, uint8_t addr, uint8_t *data, uint32_t len)
{
    uint32_t i = 0;
    bool success = false;

    success = I2C_MasterSlaveRW(enCh, addr, true);

    for(i = 0; i < len; i++){
        if((i + 1) < len){
            I2C_SetFunc(enCh,  I2cAck_En);
            I2C_ClearIrq(enCh);
            if(!I2C_Transfer(enCh, i2cStateWFData)){
                success = false;
            }
        } else {
            I2C_ClearFunc(enCh,  I2cAck_En);
            I2C_ClearIrq(enCh);
            if(!I2C_Transfer(enCh, i2cStateWFStopSent)){
                success = false;
            }
        }
        data[i] = I2C_ReadByte(enCh);
    }

    return success;
}

/*******************************************************************************
 * @brief
 *   Initalize basic I2C master mode driver.
 *
 * @details
 *   This driver only supports master mode, single bus-master.
 *
 * @param[in] init
 *   Pointer to I2C initialization structure.
 ******************************************************************************/
bool BSP_I2C_Init(BSP_I2C_TypeDef *ptr)
{
    M0P_I2C_TypeDef* i2c_ch = M0P_I2C0;
    stc_gpio_cfg_t stcGpioCfg;
    stc_i2c_cfg_t stcI2cCfg;
    stc_sysctrl_clk_cfg_t stcCfg;

    DDL_ZERO_STRUCT(stcCfg);
    DDL_ZERO_STRUCT(stcI2cCfg);
    DDL_ZERO_STRUCT(stcGpioCfg);

    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
    if(0 == ptr->idx){
        i2c_ch = M0P_I2C0;
        Sysctrl_SetPeripheralGate(SysctrlPeripheralI2c0, TRUE);
    } else {
        i2c_ch = M0P_I2C1;
        Sysctrl_SetPeripheralGate(SysctrlPeripheralI2c1, TRUE);
    }

    stcGpioCfg.enDir = GpioDirOut;
    stcGpioCfg.enOD = GpioOdEnable;
    stcGpioCfg.enPu = GpioPuEnable;
    stcGpioCfg.enPd = GpioPdDisable;

    Gpio_Init(ptr->scl_port, ptr->scl_pin, &stcGpioCfg);
    Gpio_Init(ptr->sda_port, ptr->sda_pin, &stcGpioCfg);
    Gpio_SetIO(ptr->scl_port, ptr->scl_pin);
    Gpio_SetIO(ptr->sda_port, ptr->sda_pin);
    Gpio_SetAfMode(ptr->scl_port, ptr->scl_pin, ptr->af);
    Gpio_SetAfMode(ptr->sda_port, ptr->sda_pin, ptr->af);

    stcI2cCfg.u32Pclk = Sysctrl_GetPClkFreq();
    stcI2cCfg.u32Baud = ptr->baud*1000;
    stcI2cCfg.enMode  = I2cMasterMode;
    stcI2cCfg.u8SlaveAddr = 0x55;
    stcI2cCfg.bGc = FALSE;

    if(Ok == I2C_Init(i2c_ch, &stcI2cCfg)){
        return true;
    }

    return false;
}

bool BSP_I2C_Write(BSP_Slave_Param *ptr, uint8_t *data, int len)
{
    M0P_I2C_TypeDef* enCh = M0P_I2C0;
    uint32_t i = 0;
    uint8_t reglen = 0;
    bool success = false;

    if(NULL == ptr){
        return false;
    }

    if(0 != ptr->idx){
        enCh = M0P_I2C1;
    }

    if(ptr->len > 4){
        reglen = 4;
    } else {
        reglen = ptr->len;
    }

    /* Wait start success */
    I2C_SetFunc(enCh, I2cStart_En);
    success = I2C_Transfer(enCh, i2cStateStartAddrSend);
    I2C_ClearFunc(enCh, I2cStart_En);
    I2C_ClearFunc(enCh, I2cStop_En);

    /* Wait slave success */
    if(!I2C_MasterSlaveRW(enCh, ptr->addr, false)){
        success = false;
    }

    /* write register */
    for(i = 0; i < reglen; i++){
        I2C_WriteByte(enCh, ptr->regs[i]);
        I2C_ClearIrq(enCh);
        if(!I2C_Transfer(enCh, i2cStateDataSend)){
            success = false;
        }
    }

    /* Wait data success */
    for(i = 0; i < len; i++){
        I2C_WriteByte(enCh, data[i]);
        I2C_ClearIrq(enCh);
        if(!I2C_Transfer(enCh, i2cStateDataSend)){
            success = false;
        }
    }

    I2C_SetFunc(enCh, I2cStop_En);
    I2C_ClearIrq(enCh);

    return success;
}

bool BSP_I2C_Read(BSP_Slave_Param *ptr, uint8_t *data, int len)
{
    M0P_I2C_TypeDef* enCh = M0P_I2C0;
    uint8_t reglen = 0;
    bool success = false;

    if(NULL == ptr){
        return false;
    }

    if(0 != ptr->idx){
        enCh = M0P_I2C1;
    }

    if(ptr->len > 4){
        reglen = 4;
    } else {
        reglen = ptr->len;
    }

    /* Wait start success */
    I2C_SetFunc(enCh, I2cStart_En);
    success = I2C_Transfer(enCh, i2cStateStartAddrSend);
    I2C_ClearFunc(enCh, I2cStart_En);

    if(reglen > 0){
        /* write register */
        I2C_ClearFunc(enCh, I2cStop_En);
        if(!I2C_MasterWrite(enCh, ptr->addr, ptr->regs, reglen)){
            success = false;
        }
        /* wait restart success */
        I2C_SetFunc(enCh, I2cStart_En);
        I2C_ClearIrq(enCh);
        if(!I2C_Transfer(enCh, i2cStateRStartAddrSend)){
            success = false;
        }
        I2C_ClearFunc(enCh,I2cStart_En);
    }

    /* Slave Read */
    if(!I2C_MasterRead(enCh, ptr->addr, data, len)){
        success = false;
    }

    I2C_SetFunc(enCh, I2cStop_En);
    I2C_ClearIrq(enCh);

    return success;
}

